package main.social.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import main.social.dao.GenericDao;

import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.hibernate.*;
import org.hibernate.criterion.*;

public abstract class AbstractGenericDao<T, PK extends Serializable>
		extends HibernateDaoSupport implements GenericDao<T, PK>{

	private final Class<T> persistentClass;

	@SuppressWarnings("unchecked")
	public AbstractGenericDao() {
		this.persistentClass = (Class<T>)
				((ParameterizedType)getClass().
				getGenericSuperclass()).getActualTypeArguments()[0];
		assert this.persistentClass != null;
	}

	protected final Class<T> getPersistentClass() {
		return persistentClass;
	}

	/**
	 * This method should be overridden in a subclass to change
	 *  the ordering of the basic find methods.
	 * @return a hibernate Order or null if not needed
	 */
	protected Order[] getOrderBy(){
		return null;
	}

	/**
	 * This method should be overridden in a subclass if the rows are also sorted by a property in an associated entity.
	 * @param parentCriteria the criteria created for the entity to be searched.
	 * @return the parentCriteria with the array of the orders by the properties defined in the entities associated with the parent entity if any.
	 */
	protected Criteria addOrderByAssociation(Criteria parentCriteria) {
		return parentCriteria;
	}

	@SuppressWarnings("unchecked")
	public T get(PK id) {
		assert id != null;
		return (T)getHibernateTemplate().
				get(getPersistentClass(), id);
	}

	@SuppressWarnings("unchecked")
	public T load(PK id) {
		assert id != null;
		return (T)getHibernateTemplate().
				load(getPersistentClass(), id);
	}

	@SuppressWarnings("unchecked")
	public T getById(final Long id) {
		assert id != null;
		return (T) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session) throws HibernateException {
						Criteria criteria = session.createCriteria(getPersistentClass());
						criteria.add(Restrictions.eq("id", id));
						return criteria.uniqueResult();
					}
				}
		);
	}

	public long getCount() {
		// XXX: get long value
		Integer i = (Integer)getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session) throws HibernateException {
						Criteria criteria = session.createCriteria(getPersistentClass());
						criteria.setProjection(Projections.rowCount());
						return criteria.uniqueResult();
					}
				}
		);
		return i.longValue();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> findAll() {
		List list = (List)getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session) throws HibernateException {
						Criteria criteria = session.createCriteria(getPersistentClass());
						return criteria.list();
					}
				}
		);
		return list;
	}

	public void delete(T entity) {
		assert entity != null;
		getHibernateTemplate().delete(entity);
	}

	public void flush() {
		getHibernateTemplate().flush();
	}

	@SuppressWarnings("unchecked")
	public T merge(T entity) {
		assert entity != null;
		return (T)getHibernateTemplate().merge(entity);
	}

	public void refresh(T entity) {
		assert entity != null;
		getHibernateTemplate().refresh(entity);
	}

	public void save(T entity) {
		assert entity != null;
		getHibernateTemplate().save(entity);
	}

	public void saveOrUpdate(T entity) {
		assert entity != null;
		getHibernateTemplate().saveOrUpdate(entity);
	}

	public void update(T entity) {
		assert entity != null;
		getHibernateTemplate().update(entity);
	}

}
